/*
 * Copyright 1999-2010 The msnapi.com
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.imincloud.log4j;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;

import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.ThrowableInformation;

public class MsnAppender extends AppenderSkeleton {

	/**
	 * server
	 */
	private String server = "http://www.msnapi.com";

	/**
	 * api key
	 */
	private String apikey;

	/**
	 * mids. how to get mid ,see www.msnapi.com
	 */
	private String mids;

	/**
	 * interval seccond
	 */
	private int interval = 300;

	/**
	 * message title
	 */
	private String title = "log4j notify";
	
	private boolean sendOffLineMsg = false;

	/**
	 * ArrayList holding the buffer of Logging Events.
	 */
	protected ArrayList buffer = new ArrayList();

	/**
	 * The last send message time. use it to control send frz
	 */
	private long lastSendTime = 0;

	private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

	private final Thread dispatcher;

	public MsnAppender() {
		dispatcher = new Thread(new MsnSender());
		dispatcher.setDaemon(true);
		dispatcher.start();
	}

	protected void append(LoggingEvent event) {
		long now = System.currentTimeMillis();
		if ((now - lastSendTime) > 120000) {
			String content = sdf.format(new Date()) + " " + event.getLevel()
					+ " [" + event.getLoggerName() + "]\n"
					+ event.getRenderedMessage() + "\n";

			if (event.getThrowableInformation() != null) {
				ThrowableInformation ti = event.getThrowableInformation();
				String[] throwableStrRep = ti.getThrowableStrRep();
				if (throwableStrRep != null) {
					int k = throwableStrRep.length;
					if (k > 3) {
						k = 3;
					}
					for (int i = 0; i < k; i++) {
						content += throwableStrRep[i] + "\n";
					}

				}
			}
			final String message = content;
			Thread thread = new Thread(new Runnable() {
				public void run() {
					sendMessage(message);

				}
			});
			thread.start();

		} else {
			buffer.add(event);
		}
	}

	protected void sendBuffer() {
		if (mids != null) {
			ArrayList temp = buffer;
			buffer = new ArrayList();

			String content = "found " + temp.size() + " logs\n summary\n";
			for (int i = 0, k = temp.size() > 5 ? 5 : temp.size(); i < k; i++) {
				LoggingEvent e = (LoggingEvent) temp.get(i);
				content += e.getLoggerName() + ": " + e.getMessage();
			}
			sendMessage(content);
		}

	}

	private void sendMessage(String content) {
		if (content.length() > 400) {
			content = content.substring(0, 400);
		}
		try {
			title = URLEncoder.encode(title, "utf-8");
			content = URLEncoder.encode(content, "utf-8");

			String urlAddress = server+"/api/send.do?format=txt&apikey=" + apikey + "&mids=" + mids+"&mode="+(sendOffLineMsg?2:1)
					+ "&title=" + title + "&content=" + content;

			String result = httpGet(urlAddress);
			
			String[] lines = result.split("\n");
			for(int i=0;i<lines.length;i+=2){
				String mid = getValue(lines[i]);
				int status = getIntValue(lines[i+1]);
				if(status==-2){
					//add user
					addUser(mid);
					//send it agaain
					httpGet(urlAddress);
				}else if(status == 1){
					//ok
				}else{
					System.err.println("send message msn log4j failed mid="+mid+" ,status= "+status);
				}
			}
			
			lastSendTime = System.currentTimeMillis();

		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private void addUser(String mid){
		String urlAddress = server+"/api/adduser.do?format=txt&apikey=" + apikey + "&mid=" + mid;
		
		try {
			String result = httpGet(urlAddress);
			
			String code = getValue(result);
			if(!"1".equals(code)){
				System.err.println("add user "+mid+" error,code = "+code);
			}
		} catch (Exception e) {
			System.err.println("add user "+mid+" error"+e.getMessage());
		}
	}
	
	private int getIntValue(String string){
		return Integer.parseInt(getValue(string));
	}
	
	private String getValue(String string){
		int i = string.indexOf("=");
		if(i>0){
			return string.substring(i+1,string.length()).trim();
		}else{
			return null;
		}
	}

	public String httpGet(String urlAddress) throws IOException {
		URL url = new URL(urlAddress);

		InputStream in = null;
		StringBuilder sb = new StringBuilder();
		try {
			URLConnection c = url.openConnection();
			in = c.getInputStream();

			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String line;
			while ((line = br.readLine()) != null) {
				sb.append(line).append("\n");
			}
		} finally {
			try {
				if (in != null)
					in.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		return sb.toString();
	}

	public void close() {
		this.closed = true;
	}

	public boolean requiresLayout() {
		return false;
	}

	public class MsnSender implements Runnable {
		public void run() {
			while (!MsnAppender.this.closed) {
				try {
					sendBuffer();
				} catch (Exception e1) {
					e1.printStackTrace();
				}

				// sleep
				try {
					Thread.sleep(interval * 1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}

	}

	/////////////////// getter and setter ////////////////////
	
	public String getMids() {
		return mids;
	}

	public void setMids(String mids) {
		this.mids = mids;
	}

	public int getInterval() {
		return interval;
	}

	public void setInterval(int interval) {
		this.interval = interval;
	}

	public String getServer() {
		return server;
	}

	public void setServer(String server) {
		this.server = server;
	}
	
	public String getApikey() {
		return apikey;
	}

	public void setApikey(String apikey) {
		this.apikey = apikey;
	}
	
	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public boolean isSendOffLineMsg() {
		return sendOffLineMsg;
	}

	public void setSendOffLineMsg(boolean sendOffLineMsg) {
		this.sendOffLineMsg = sendOffLineMsg;
	}
}
